基于官方文档总结
[TOC]
Vue
允许声明式地将 DOM 绑定至底层组件实例的数据。
使用<span>Message: {{ msg }}</span>
语法绑定组件实例中的对应的 property 的值。
使用v-once
指令进行一次性插值
原始 HTML
文本插值会解释为普通文本,如果需要输出 HTML,使用v-html
指令:
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
<!--将整个v-html作用的标签 替换作为html内容-->
属性 Attribute
用于动态修改标签属性的值,使用v-bind
指令:
<img v-bind:src="img" alt="img" />
<img :src="img" alt="img" />
说明:如果绑定的值是null
或undefined
,那么该 attribute 将不会被包含在渲染的元素上。(bool 属性有所区别)
使用JS表达式
对于所有的数据绑定,Vue 都提供了完全的 JavaScript 表达式支持(单个表达式)
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
事件中也可以使用JS表达式
常见指令如下,有的指令可以接收参数(冒号后)
v-html
v-bind(可以缩写:
)
v-model 在表单<input>
、<textarea>
及<select>
等元素上创建双向数据绑定。
v-if
v-for
v-on(可以缩写@
)
指令参数也是支持 JS 表达式,语法:
<a v-bind:[attributeName]="url"> ... </a>
组件的data
选项是函数,创建组件实例时返回一个对象,并以$data
存储在组件实例中。
同时该对象的顶级属性 property 会直接通过组件示例暴露处理。
组件的methods
选项为包含组件所需要的方法的对象,Vue 自动为其绑定了this
,始终指向组件实例。定义方法时避免使用箭头函数。
const app = Vue.createApp({
data() {
return { count: 4 }
},
methods: {
increment() {
// `this` 指向该组件实例
this.count++
}
}
})
const vm = app.mount('#app')
console.log(vm.count) // => 4
vm.increment()
console.log(vm.count) // => 5
data 返回对象的原因:
组件是可复用的,通过函数局部作用域创建并返回的对象,每个组件实例是不同的,否则组件之间的数据可能干扰。
对于包含响应式数据的复杂逻辑,应该使用计算属性
在配置对像中的computed
对象中定义计算属性(其实是一个函数并返回结果)。使用时可以像普通属性一样将数据(计算属性的结果)绑定到模版中的计算属性。
好处:一次声明Vue可以知道该计算属性依赖的真实属性,当依赖属性发送改变时,依赖该计算属性的绑定都会更新。与使用方法相比,计算属性可以缓存结果。
<div id="computed-basics">
<span>{{ computedMsg }}</span>
</div>
Vue.createApp({
data(){...}
computed: {
// 计算属性
computedMsg() {
return this.msg.split('').reverse().join('');
},
},
}).mount('#computed-basics')
watch
选项提供类一个更加通用方式来响应数据的变化。用于执行异步操作,或者开销比较大的操作。
...
watch: {
// 每当 message 发生变化时,该函数将会执行
message(newValue, oldValue) {
if(newValue.length()>oldValue.length()){
this.getNewMessage()
}
}
},
...
深度侦听
...
watch: {
"user.name": { // 使用字符串,仅仅单独侦听该对象的某个属性
immediate: true, // 初始化时也会调用函数
deep: true, // 深度侦听,用于侦听对象中的属性变化
handler: function(newValue){
console.log(newValue);
}
}
},
...
操作元素的 class 列表和内联样式其实也是一种数据绑定,绑定元素的 attribute。
使用 v-bind 绑定class | style
,表达式结果可以是字符串、对象以及数组。
例如使用 data property 中的对象进行绑定:
<div :class="classObject"></div> <!-- v-bind:class 简写 -->
data() {
return {
classObject: { // 作为绑定的对象
active: true,
'text-danger': false
}
}
}
或者可以绑定一个返回对象的计算属性;数组和对象也可以结合使用。
内联样式绑定与之类似。CSS property 名用驼峰式或短横线分隔 (用引号括起来) 来命名。
v-if/v-else/v-else-if
有条件的渲染元素/分组(使用<template>
标签包裹)
v-show
使用v-if/v-else
,v-if 中表达式返回 truthy 值的时候被渲染,否则渲染 v-else 元素
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的display
CSS property。它不支持<template>
元素,也不支持v-else
v-if
更多适用与一次渲染或者条件改变不频繁的场景,v-show
可用于条件频繁改变场景
使用v-for
进行列表渲染数组
<ul id="array-with-index">
<!--(item, index) of items-->
<li v-for="(item, index) in items" :key="item.id">
{{ index }} - {{ item.message }}
</li>
</ul>
也可以渲染一个对象,第一个参数是对象键值,第二个参数是键名(可选的第三个参数 index)。注意遍历对象时使用的Object.keys()
结果遍历。
注意:Vue 默认使用就地更新的策略,如果数据项顺序发生改变,并不会移动 DOM 元素来匹配数据项的位置顺序,只是就地更新每个元素。因此应该尽可能在使用v-for
时提供:key
作为标识来追踪该节点的身份(应该应用哪个数据项)。
Vue 将侦听的数组的变更方法进行了包裹,使用会自动触发视图的更新。
显示过滤/排序后的结果?
可以使用计算属性,不实际变更或重置原始数据,只是返回过滤/排序后的数组。或者在嵌套的 v-for 循环中使用 methods 。
v-for 中可以使用整数,<span v-for="n in 10" :key="n">{{ n }} </span>
重复模版 10次
利用<template>和v-for
循环渲染一段包含多个元素的内容
不推荐在同一元素上使用v-if
和v-for
。当它们处于同一节点,v-if
的优先级比v-for
更高,这意味着v-if
将没有权限访问v-for
里的变量。
推荐写法:使用<template>
<template v-for="todo in todos" :key="todo.name">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
使用v-on
指令 (通常缩写为@
符号) 来监听 DOM 事件,并在触发事件时执行一些 JavaScript。用法为v-on:click="methodName"
或使用快捷方式@click="methodName"
事件处理写法:
直接内联 JS 代码
定义 Methods 中的(事件处理)方法,直接绑定一个方法的名称
在内联 JavaScript 语句中调用方法(12结合);用特殊变量$event
表示原始 DOM event对象
事件修饰符:
.stop
阻止事件继续冒泡
.prevent
阻止默认行为,例如提交事件会重载页面
.once
只能触发一次处理函数
.self
只当在 event.target 是当前元素自身时触发处理函数
.capture
内部元素触发的事件先在此处理
用v-model指令在表单<input>
、<textarea>
及<select>
元素上创建双向数据绑定。
本质就是:绑定数据,并且监听用户的输入数据来更新对应的数据。
v-model
不会在输入法组织文字过程中得到更新。使用input
事件监听器和value
绑定来替代v-model
可以响应。
【会忽略表单元素 attr 上的初始值,可以在组件的 data 选项中声明其初始值】
<input v-model="message" placeholder="edit me" />
<p>Message is: {{ message }}</p>
常见表单用法:
https://v3.cn.vuejs.org/guide/forms.html#文本-text
.lazy
v-model 默认每次input
事件触发后将输入框的值与数据进行同步;使用 .lazy 修饰转为在change
事件之后进行同步
.number
将用户的输入值转为数值类型;当输入类型为text
时这通常很有用,如果值无法被parseFloat()
解析,则返回原始值
.trim
自动过滤用户输入的首尾空白字符
组件是带名称的可复用的实例。都接收相同的选项,例如data
、computed
、watch
、methods
以及生命周期钩子等。
说明:为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。
使用 Prop
在组件上注册一些自定义 attribute 即 Prop,使用props
选项来定义该组件可接受的 prop 列表。父组件通过该 prop 来向该子组件传值,该 property 的值可以在该组件的模板中访问。
传值,可以使用v-bind
或:
来动态传值:
<div id="blog-posts-demo">
<blog-post v-for="post in posts" :key="post.id" :title="post.title">
</blog-post>
</div>
定义props
简单方式是定义 prop 数组,也可以定对象来为 prop 指定验证要求:
export default {
props: {
title: {
type: String,
default: 'Hello !!!',
required: true,
}
}
}
注意:对象/数组的默认值,必须从一个工厂函数返回。
单向数据流
单向下行绑定,父级 prop 的更新会向下流动到子组件中,但是反过来则不行
不能直接使用 Prop 方式传递,父组件可以监听子组件自定义的事件来接收传递的数据。
子组件中:
// 在方法中定义触发的事件
methods:{
sendParent(){
// $emit 来触发事件
this.$emit("sendMsg", this.msg);
}
},
...
父组件中:
// 在子组件元素上监听事件
<template>
<ChildComponent @sendMsg="getChildMsg"></ChildComponent>
</template>
在 JavaScript 中直接访问子组件,可以使用ref
attribute 为子组件或 HTML 元素指定引用 ID:
<base-input ref="usernameInput"></base-input>
以 JS 方式访问元素(DOM),例如以编程的方式 focus 到这个 input 。
父组件直接访问子组件:$refs
父组件中定义的(子组件)元素上使用ref="usernameInput"
添加引用名称后,可以使用$refs.usernameInput
来获取引用元素.
$refs
只会在组件渲染完成之后生效,避免在模板或计算属性中访问
子组件访问父组件:$parent
子组件中使用$parent
可以获取父组件对象信息。
由于组件是复用的,从而父组件可能是不同的,不建议使用该方式获取数据。而建议使用 Prop 来传递数据。
访问根组件:$root
类似与 HTML 中向一个标签传递值:
<alert-box>Something bad happened.</alert-box>
在组件中可以利用**<slot></slot>
**来作为插入内容的占位符,在使用该组件元素时(使用双标签语法)可以进行填充。
<Component1><button>111</button></Component1>
<Component1><p>hello world!</p></Component1>
如何定义多个插槽?
具名插槽,定义 slot 时添加 name 属性,使用组件元素时使用<templat> 和 v-slot指令
包裹每个插槽对应的内容:
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
作用域插槽
为了某些场景(自定义渲染列表的方式),插槽的中数据内容可以由子组件提供,使用**v-slot:default="slotProps"
** 来接收子组件定义插槽时传递的所有数据对象 slotProps
<template v-slot:default="slotProps">
<ul>
<li v-for="item in slotProps.list" :key="item">{{item}}</li>
</ul>
</template>
如果组件层次结构较深,使用provide, inject
,父组件可以作为其所有子组件的依赖提供者。
父组件有一个provide
选项来提供数据,子组件有一个inject
选项来开始使用这些数据。作用类似于 Prop 数据的传递。
// 祖先组件的 provide 选项
provide: {
user: 'John Doe'
},
// 子组件使用 inject 选项
inject: ['user'],
注意:如果 provide 选项是一个对象,则不能直接访问组件实例的 property。
为了使用组件实例的 property,则 provide 选项需要改为返回对象的函数。
响应性问题
即使这样,访问的 property 也不是响应性绑定,即祖先数据的变化不会反映在子组件 inject 中的 property。默认的 provide/inject 绑定不是响应式的。
resolve: 可以通过传递一个ref
property 或reactive
对象给provide
来改变这种行为
provide(){
return{
obj: this.obj, // 接收的是包含数据的对象(修改也是修改该对象中的数据)
}
},
provide(){
return{
message: ()=>this.message, // 接收的是函数,调用才返回响应式数据
}
},
子组件多次调用中,可以使用计算属性来缓存该结果。
computed
使用:
组件视图更新(绑定数据更新)会执行对应的beforeUpdate,updated
函数
组件的销毁和创建,会执行beforeUnmount,unmounted
函数,例如使用 v-if 进行条件渲染。
作用:将同一个逻辑关注点相关代码收集在一起。
setup
选项是一个接收props/context
的函数,在组件创建之前执行(可以代替 beforeCreate, created 生命周期钩子)。并且不能使用 this 来访问组件实例。
setup
返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板(模版中使用的引用变量会自动浅解包)。
setup 选项中定义的变量默认是非响应式的(修改不会影响视图),使用ref
函数接收参数并将其包裹在一个带有value
property 的对象中返回,使用该 property 可以访问/修改该响应式变量值。因为JS中Number
或String
等基本类型是通过值而非引用传递,这样封装也是为了不同数据类型的行为统一。
ref()
响应式变量对Number, String
使用 ref() ,返回响应式对象,通过value
属性访问值。
数组对象也通常使用 ref()
对于对象,使用reactive()
和toRefs()
对象使用 reactive() 返回代理对象(响应式)
为了直接暴露对象的属性名称(来访问值),使得通过该属性名称访问也是响应式的,使用 toRefs() 与对象解构用法:toRefs() 返回一个对象,每个属性值都是原属性的响应式对象 ObjectRefImpl (方法需要一个响应式对象参数)
ref() 返回的是 RefImpl,如果参数是对象,其value就是该对象的代理对象。reactive()直接返回对象的代理对象Proxy。一般对象使用 reactive 更加方便。
// setup()
...
const user = reactive({
name: 'xiaoming',
age: 20,
});
const { name, age } = toRefs(user);
function changName() {
// console.log(name.value);
name.value = 'zhou123';
}
function changAge() {
age.value = 22;
}
return{ name, age, changeName, changeAge}
组合式 API,从 vue 导入watch
函数执行相同的操作。
一个想要侦听的响应式引用或 getter 函数
一个回调
可选的配置选项
注意 watch 函数只能监听getter 函数/ ref / reactive 对象
,响应式对象中的属性改变会调用回调函数(但不能获取属性旧值)。
另一个是 watchEffect() ,自动分析依赖,发生改变后触发,不能获取旧值。
//setup()
const user = reactive({
name: 'aaa',
age: 18,
});
changeUserName(){
user.name = 'bbb';
}
watch(user, (newVal, oldVal)=>{
...
});
watchEffect(()=>{
console.log(user.name);
});
组合式 API,从 vue 导入computed
函数执行相同的操作。
返回一个带有 value 属性的对象。模版中可以直接使用该名称。
// setup()
const reverseMsg = computed(()=>{
return msg.value.split('').reverse().join('')
})
setup() 中不能使用 this,setup(props, context) 有
setup
函数中的props
也是响应式的,当传入新的 prop 时,它将被更新。为组件接收的参数。因为props
是响应式的,不能使用 ES6 解构,它会消除 prop 的响应性,使用toRefs
函数完成。
context
是一个普通 JavaScript 对象,暴露了其它可能在setup
中有用的值:
等同于 $attrs,获取组件标签上定义的属性
context.slots
非响应式对象,等同于 $slots
context.emit
方法,等同于 $emit,用来触发事件
context.expose
函数,暴露公共 property 。如果组件的 setup() 返回渲染函数(替代template),它用来暴露组件的公共 property。组件元素上定义ref="refName"
,外部组件使用 $refs 来访问(模板 ref)。
https://v3.cn.vuejs.org/guide/composition-api-lifecycle-hooks.html
在setup()
中使用provide
,导入该方法即可。
在setup()
中使用inject
时,也需要从vue
显式导入。
可以在 provide 值时使用ref 或reactive增加值之间的响应性。
ref 属性
reactive 对象
组合式 APIcomputed
// ref
let counter = ref(0)
//
let user = reactive({
name: 'abc',
age: 20,
})
let len = Vue.computed(() => this.todos.length)
provide('counter', counter)
provide('user', user)
provide('len', len)
<script setup>
语法糖,相当于 set() 函数的定义,特殊点:
导入的子模块不需要注册,可以直接在模版中使用
顶层的绑定(变量/函数,导入的函数等)会直接暴露给模版使用(即定义的变量不需要通过 return 暴露)
定义响应式变量,也需要从vue
中导入
prop 可以使用defineProps()
宏来定义
本文链接: Vue 基础
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
发布日期: 2022-06-09
最新构建: 2024-12-26
欢迎任何与文章内容相关并保持尊重的评论😊 !